#! /bin/sh /usr/share/dpatch/dpatch-run
## 13_avahi_fix_and_handle_restart.dpatch by  <jblache@debian.org>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: Fix Avahi polling:
## DP:  - use ThreadedPoll instead of SimplePoll
## DP:  - lock the ThreadedPoll when adding services
## DP:  - start rendez-vous thread after daemonizing, so the thread actually
## DP:    runs after we've daemonized.
## DP: Also handle Avahi daemon restarts, as it's good practice and easy to do.
## DP:
## DP:  ... And /that/, my friends, is how we do that ...

@DPATCH@
diff -urNad mt-daapd-0.9~r1696.dfsg~/src/main.c mt-daapd-0.9~r1696.dfsg/src/main.c
--- mt-daapd-0.9~r1696.dfsg~/src/main.c	2009-04-07 13:38:58.307851767 +0200
+++ mt-daapd-0.9~r1696.dfsg/src/main.c	2009-04-07 13:38:58.763851626 +0200
@@ -485,6 +485,12 @@
 
     runas = conf_alloc_string("general","runas","nobody");
 
+    if(!os_init(config.foreground,runas)) {
+        DPRINTF(E_LOG,L_MAIN,"Could not initialize server\n");
+        os_deinit();
+        exit(EXIT_FAILURE);
+    }
+
 #ifndef WITHOUT_MDNS
     if(config.use_mdns) {
         DPRINTF(E_LOG,L_MAIN,"Starting rendezvous daemon\n");
@@ -495,12 +501,6 @@
     }
 #endif
 
-    if(!os_init(config.foreground,runas)) {
-        DPRINTF(E_LOG,L_MAIN,"Could not initialize server\n");
-        os_deinit();
-        exit(EXIT_FAILURE);
-    }
-
     free(runas);
 
 #ifdef UPNP
diff -urNad mt-daapd-0.9~r1696.dfsg~/src/rend-avahi.c mt-daapd-0.9~r1696.dfsg/src/rend-avahi.c
--- mt-daapd-0.9~r1696.dfsg~/src/rend-avahi.c	2009-04-07 13:38:42.000000000 +0200
+++ mt-daapd-0.9~r1696.dfsg/src/rend-avahi.c	2009-04-07 13:39:12.790934153 +0200
@@ -2,6 +2,7 @@
  * Rendezvous support with avahi
  *
  * Copyright (C) 2005 Sebastian Dröge <slomo@ubuntu.com>
+ * Copyright (C) 2009 Julien BLACHE <jb@jblache.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -37,7 +38,7 @@
 #include <avahi-client/client.h>
 #include <avahi-common/alternative.h>
 #include <avahi-common/error.h>
-#include <avahi-common/simple-watch.h>
+#include <avahi-common/thread-watch.h>
 #include <avahi-common/timeval.h>
 #include <avahi-common/malloc.h>
 
@@ -46,7 +47,7 @@
 
 static AvahiClient *mdns_client = NULL;
 static AvahiEntryGroup *mdns_group = NULL;
-static AvahiSimplePoll *simple_poll = NULL;
+static AvahiThreadedPoll *threaded_poll = NULL;
 
 typedef struct tag_rend_avahi_group_entry {
     char *name;
@@ -59,14 +60,6 @@
 
 static REND_AVAHI_GROUP_ENTRY rend_avahi_entries = { NULL, NULL, 0, NULL };
 
-static pthread_t rend_tid;
-static pthread_cond_t rend_avahi_cond;
-static pthread_mutex_t rend_avahi_mutex;
-
-static void _rend_avahi_signal(void);
-static void _rend_avahi_wait_on(void *what);
-static void _rend_avahi_lock(void);
-static void _rend_avahi_unlock(void);
 static int _rend_avahi_create_services(void);
 
 /* add a new group entry node */
@@ -83,36 +76,21 @@
     pge->txt = strdup(txt);
     pge->port = port;
 
-    _rend_avahi_lock();
-
     pge->next = rend_avahi_entries.next;
     rend_avahi_entries.next = pge;
 
-    _rend_avahi_unlock();
     return 1;
 }
 
-static void *rend_poll(void *arg) {
-    int ret;
-    while((ret = avahi_simple_poll_iterate(simple_poll,-1)) == 0);
-
-    if(ret < 0) {
-        DPRINTF(E_WARN,L_REND,"Avahi poll thread quit iwth error: %s\n",
-                avahi_strerror(avahi_client_errno(mdns_client)));
-    } else {
-        DPRINTF(E_DBG,L_REND,"Avahi poll thread quit\n");
-    }
-
-    return NULL;
-}
-
 static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
     //    assert(g == mdns_group);
 
+    if (!g || (g != mdns_group))
+        return;
+
     switch (state) {
     case AVAHI_ENTRY_GROUP_ESTABLISHED:
         DPRINTF(E_DBG, L_REND, "Successfully added mdns services\n");
-        _rend_avahi_signal();
         break;
     case AVAHI_ENTRY_GROUP_COLLISION:
         DPRINTF(E_DBG, L_REND, "Group collision\n");
@@ -125,7 +103,7 @@
         */
         break;
     case AVAHI_ENTRY_GROUP_FAILURE :
-        avahi_simple_poll_quit(simple_poll);
+        avahi_threaded_poll_quit(threaded_poll);
         break;
     case AVAHI_ENTRY_GROUP_UNCOMMITED:
     case AVAHI_ENTRY_GROUP_REGISTERING:
@@ -133,34 +111,9 @@
     }
 }
 
-void _rend_avahi_lock(void) {
-    if(pthread_mutex_lock(&rend_avahi_mutex))
-        DPRINTF(E_FATAL,L_REND,"Could not lock mutex\n");
-}
-
-void _rend_avahi_unlock(void) {
-    pthread_mutex_unlock(&rend_avahi_mutex);
-}
-
-void _rend_avahi_signal(void) {
-    /* kick the condition for waiters */
-    _rend_avahi_lock();
-    pthread_cond_signal(&rend_avahi_cond);
-    _rend_avahi_unlock();
-}
-
-void _rend_avahi_wait_on(void *what) {
-    DPRINTF(E_DBG,L_REND,"Waiting on something...\n");
-    if(pthread_mutex_lock(&rend_avahi_mutex))
-        DPRINTF(E_FATAL,L_REND,"Could not lock mutex\n");
-    while(!what) {
-        pthread_cond_wait(&rend_avahi_cond,&rend_avahi_mutex);
-    }
-    _rend_avahi_unlock();
-    DPRINTF(E_DBG,L_REND,"Done waiting.\n");
-}
-
 int rend_register(char *name, char *type, int port, char *iface, char *txt) {
+    avahi_threaded_poll_lock(threaded_poll);
+
     DPRINTF(E_DBG,L_REND,"Adding %s/%s\n",name,type);
     _rend_avahi_add_group_entry(name,type,port,iface,txt);
     if(mdns_group) {
@@ -169,6 +122,9 @@
     }
     DPRINTF(E_DBG,L_REND,"Creating service group (again?)\n");
     _rend_avahi_create_services();
+
+    avahi_threaded_poll_unlock(threaded_poll);
+
     return 0;
 }
 
@@ -186,7 +142,7 @@
     unsigned char *key,*nextkey;
     unsigned char *newtxt;
 
-    DPRINTF(E_DBG,L_REND,"Creting service group\n");
+    DPRINTF(E_DBG,L_REND,"Creating service group\n");
 
     if(!rend_avahi_entries.next) {
         DPRINTF(E_DBG,L_REND,"No entries yet... skipping service create\n");
@@ -203,10 +159,6 @@
         }
     }
 
-    /* wait for entry group to be created */
-    _rend_avahi_wait_on(mdns_group);
-
-    _rend_avahi_lock();
     pentry = rend_avahi_entries.next;
     while(pentry) {
         /* TODO: honor iface parameter */
@@ -244,14 +196,12 @@
                                                  psl)) < 0) {
             DPRINTF(E_WARN, L_REND, "Could not add mdns services: %s\n", avahi_strerror(ret));
             avahi_string_list_free(psl);
-            _rend_avahi_unlock();
+
             return 0;
         }
         pentry = pentry->next;
     }
 
-    _rend_avahi_unlock();
-
     if ((ret = avahi_entry_group_commit(mdns_group)) < 0) {
         DPRINTF(E_WARN, L_REND, "Could not commit mdns services: %s\n",
                 avahi_strerror(avahi_client_errno(mdns_client)));
@@ -262,6 +212,8 @@
 }
 
 static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
+    int error;
+
     assert(c);
     switch(state) {
     case AVAHI_CLIENT_S_RUNNING:
@@ -276,7 +228,29 @@
         break;
     case AVAHI_CLIENT_FAILURE:
         DPRINTF(E_LOG,L_REND,"Client failure\n");
-        avahi_simple_poll_quit(simple_poll);
+
+	error = avahi_client_errno(c);
+	if (error == AVAHI_ERR_DISCONNECTED)
+	  {
+	    DPRINTF(E_LOG,L_REND,"Server disconnected, reconnecting\n");
+
+	    avahi_client_free(mdns_client);
+	    mdns_group = NULL;
+
+	    mdns_client = avahi_client_new(avahi_threaded_poll_get(threaded_poll),
+					   AVAHI_CLIENT_NO_FAIL,
+					   client_callback,NULL,&error);
+	    if (mdns_client == NULL)
+	      {
+		DPRINTF(E_LOG,L_REND,"Failed to create new Avahi client: %s\n", avahi_strerror(error));
+		avahi_threaded_poll_quit(threaded_poll);
+	      }
+	  }
+	else
+	  {
+	    DPRINTF(E_LOG,L_REND,"Client failure: %s\n", avahi_strerror(error));
+	    avahi_threaded_poll_quit(threaded_poll);
+	  }
         break;
     case AVAHI_CLIENT_S_REGISTERING:
         DPRINTF(E_LOG,L_REND,"Client registering\n");
@@ -284,6 +258,7 @@
             avahi_entry_group_reset(mdns_group);
         break;
     case AVAHI_CLIENT_CONNECTING:
+        DPRINTF(E_LOG,L_REND,"Client connecting\n");
         break;
     }
 }
@@ -291,18 +266,8 @@
 int rend_init(char *user) {
     int error;
 
-    if(pthread_cond_init(&rend_avahi_cond,NULL)) {
-        DPRINTF(E_LOG,L_REND,"Could not initialize rendezvous condition\n");
-        return -1;
-    }
-
-    if(pthread_mutex_init(&rend_avahi_mutex,NULL)) {
-        DPRINTF(E_LOG,L_REND,"Could not initialize rendezvous mutex\n");
-        return -1;
-    }
-
     DPRINTF(E_DBG, L_REND, "Initializing avahi\n");
-    if(!(simple_poll = avahi_simple_poll_new())) {
+    if(!(threaded_poll = avahi_threaded_poll_new())) {
         DPRINTF(E_LOG,L_REND,"Error starting poll thread\n");
         return -1;
     }
@@ -316,26 +281,37 @@
         mdns_interface = AVAHI_IF_UNSPEC;
     */
 
-    if (!(mdns_client = avahi_client_new(avahi_simple_poll_get(simple_poll),
-                                         0,client_callback,NULL,&error))) {
+    if (!(mdns_client = avahi_client_new(avahi_threaded_poll_get(threaded_poll),
+                                         AVAHI_CLIENT_NO_FAIL,
+					 client_callback,NULL,&error))) {
         DPRINTF(E_WARN, L_REND, "avahi_client_new: Error in avahi: %s\n",
                 avahi_strerror(avahi_client_errno(mdns_client)));
-        avahi_simple_poll_free(simple_poll);
+        avahi_threaded_poll_free(threaded_poll);
         return -1;
     }
 
-    DPRINTF(E_DBG, L_REND, "Starting avahi polling thread\n");
-    if(pthread_create(&rend_tid, NULL, rend_poll, NULL)) {
-        DPRINTF(E_FATAL,L_REND,"Could not start avahi polling thread.\n");
-    }
+    DPRINTF(E_DBG, L_REND, "Starting Avahi ThreadedPoll\n");
+    if (avahi_threaded_poll_start(threaded_poll) < 0)
+      {
+	DPRINTF(E_WARN, L_REND, "avahi_threaded_poll_start: error: %s\n",
+		avahi_strerror(avahi_client_errno(mdns_client)));
+
+	avahi_threaded_poll_free(threaded_poll);
+
+	return -1;
+      }
 
     return 0;
 }
 
 int rend_stop() {
-    avahi_simple_poll_quit(simple_poll);
+    avahi_threaded_poll_stop(threaded_poll);
+
     if (mdns_client != NULL)
         avahi_client_free(mdns_client);
+
+    avahi_threaded_poll_free(threaded_poll);
+
     return 0;
 }
 
